package com.peter.ai.voice;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.OutputFormatEnum;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizer;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerListener;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerResponse;
import com.aliyuncs.exceptions.ClientException;
import com.peter.ai.util.AliyunCreateTokenUtil;
import com.peter.ai.util.PCM2WAV;

/**
 * tts 支持最多300个字符,此demo展示超过300字符的调用方式
 *
 * @author zhishen.ml
 * @date 2018-06-12
 */
public class AliClientInstance {
	private String appKey = "OPDYyNXtBnm1E20S";;
	//private String appKey = "WcgCe8TNsjS4jf5L";;
	private String accessToken;
	NlsClient client;

	public AliClientInstance() {
		// 创建NlsClient实例,应用全局创建一个即可,默认服务地址为阿里云线上服务地址
		if (client == null) {
			if (accessToken == null) {
				try {
					this.accessToken = AliyunCreateTokenUtil.createToken();
				} catch (ClientException e) {
					e.printStackTrace();
				}
			}
			client = new NlsClient(accessToken);
		}
	}

	private static SpeechSynthesizerListener getSynthesizerListener(final FileOutputStream fout) {

		SpeechSynthesizerListener listener = null;
		try {
			listener = new SpeechSynthesizerListener() {

				// 语音合成结束
				@Override
				public void onComplete(SpeechSynthesizerResponse response) {
					System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName()
							+ ", status: " + response.getStatus());
				}

				// 语音合成的语音二进制数据
				@Override
				public void onMessage(ByteBuffer message) {
					try {
						byte[] bytesArray = new byte[message.remaining()];
						message.get(bytesArray, 0, bytesArray.length);
						System.out.println("write arrya:" + bytesArray.length);
						fout.write(bytesArray);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}

				@Override
				public void onFail(SpeechSynthesizerResponse response) {
					System.out.println("task_id: " + response.getTaskId() +
					// 状态码 20000000 表示识别成功
					", status: " + response.getStatus() +
					// 错误信息
					", status_text: " + response.getStatusText());

				}
			};
		} catch (Exception e) {
			e.printStackTrace();
		}
		return listener;
	}

	public void process(final String longText, final FileOutputStream fout) {
		List<String> textArr = splitLongText(longText, 100);
		SpeechSynthesizer synthesizer = null;
		try {
			// 创建实例,建立连接
			synthesizer = new SpeechSynthesizer(client, getSynthesizerListener(fout));
			synthesizer.setAppKey(appKey);
			// 此处一定要设置为pcm格式,才能将多次结果拼接起来
			synthesizer.setFormat(OutputFormatEnum.PCM);
			// 设置返回音频的采样率
			synthesizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);

			for (int i = 0; i < textArr.size(); i++) {
				// 设置用于语音合成的文本
				synthesizer.setText(textArr.get(i));
				// 此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认
				synthesizer.start();
				// 等待语音合成结束
				synthesizer.waitForComplete();
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭连接
			if (null != synthesizer) {
				synthesizer.close();
			}
			try {
				fout.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * 将长文本切分为每句字数不大于size数目的短句
	 *
	 * @param text
	 * @param size
	 * @return
	 */
	public static List<String> splitLongText(String text, int size) {
		// 先按标点符号切分
		String[] texts = text.split("[、，。；？！,!\\?]");
		StringBuilder textPart = new StringBuilder();
		List<String> result = new ArrayList<String>();
		int len = 0;
		// 再按size merge,避免标点符号切分出来的太短
		for (int i = 0; i < texts.length; i++) {
			if (textPart.length() + texts[i].length() + 1 > size) {
				result.add(textPart.toString());
				textPart.delete(0, textPart.length());

			}
			textPart.append(texts[i]);
			len += texts[i].length();
			if (len < text.length()) {
				textPart.append(text.charAt(len));
				len += 1;
			}

		}
		if (textPart.length() > 0) {
			result.add(textPart.toString());
		}

		return result;

	}

	public void shutdown() {
		client.shutdown();
	}

	public static void text2Voice(String text, String dirPath, String fileName) {
		try {
			// 文字合成语音 pcm
			AliClientInstance instance = new AliClientInstance();
			File out = new File(dirPath + fileName + ".pcm");
			instance.process(text, new FileOutputStream(out));
			instance.shutdown();
			// pcm转wav
			PCM2WAV.convertAudioFiles(dirPath + fileName + ".pcm", dirPath + fileName + ".mp3");
			out.delete();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
